package com.ds.lens.flink.monitor.eventsinkjob.function.notify.onduty;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ds.lens.data.common.client.vo.LensMonitorChannel;
import com.ds.lens.data.common.client.vo.LensMonitorChannelInVO;
import com.ds.lens.data.common.client.LensMonitorClient;
import com.ds.lens.data.common.client.LensResponseData;
import com.ds.lens.data.common.constant.AggregatorEnum;
import com.ds.lens.data.common.constant.ChannelEnum;
import com.ds.lens.data.common.constant.EventSourceEnum;
import com.ds.lens.data.common.constant.MonitorStateEnum;
import com.ds.lens.data.common.document.dto.MonitorEvent;
import com.ds.lens.flink.monitor.*;
import com.ds.lens.flink.monitor.common.BuilderUtil;
import com.ds.lens.flink.monitor.eventsinkjob.function.notify.MonitorEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.Map;
import java.util.Set;

@Slf4j
public class OndutyMonitorEventNotifier implements MonitorEventNotifier<MonitorEvent, String> {

    private static final String ONDUTY_CLOSED_STATUS = "closed";
    private static final String ONDUTY_RESOLVED_STATUS = "resolved";

    @Override
    public String send(MonitorEvent event) throws Exception {
        if (EventSourceEnum.EXTERNAL_EVENT.equals(event.getEventSourceEnum())) {
            return doSendExternalEvent(event);
        } else {
            return doSendLensEvent(event);
        }
    }

    private String doSendExternalEvent(MonitorEvent event) {
        return fireTicket(event);
    }

    private String doSendLensEvent(MonitorEvent event) throws Exception {
        if (null == event) {
            log.error("fail to send mag to onduty in SendNoticeByOnduty.send(). Caused by: AlarmNotice is null");
            throw new Exception("AlarmNotice is null");
        }

        String ticketId = getTicket(event);
        if (ticketId != null) {
            ticketId = appendTicket(ticketId, event);
        }

        if (ticketId == null && event.getTriggered().getState() != MonitorStateEnum.OK) {
            ticketId = fireTicket(event);
        }

        if (ticketId != null) {
            saveTicket(ticketId, event);
        }
        return ticketId;
    }

    private String getTicket(MonitorEvent event) {
        LensMonitorClient monitorClient = MonitorJobContext.getInstance().getMonitorClient();
        LensResponseData<LensMonitorChannel> resp = monitorClient.getMonitorChannel(event.getMonitorKey().getMonitorId(),
                event.getMonitorKey().getVersion(), BuilderUtil.DEFAULT_GROUP_VALUE, ChannelEnum.ON_DUTY.getValue());
        LensMonitorChannel channel = resp.getData();
        if (channel == null) {
            return null;
        }

        int ticketExpiredInMinutes = MonitorJobContext.getInstance().getTicketExpiredInMinutes();
        if (System.currentTimeMillis() - channel.getTicketTime() >= ticketExpiredInMinutes * 60 * 1000) {
            return null;
        }
        String ticketId = channel.getTicket();
        OndutyClient ondutyClient = MonitorJobContext.getInstance().getOndutyClient();
        String authorization = MonitorJobContext.getInstance().getOndutyAuthorization();
        JsonObject detail = ondutyClient.getDetail(authorization, ticketId);
        if (detail == null || detail.get("data") == null) {
            return null;
        }
        String status = detail.getAsJsonObject("data").getAsJsonPrimitive("status").getAsString();
        if (ONDUTY_CLOSED_STATUS.equals(status) || ONDUTY_RESOLVED_STATUS.equals(status)) {
            return null;
        }
        return ticketId;
    }

    private String appendTicket(String ticketId, MonitorEvent event) {
        Comment comment = new Comment(ticketId, event.getNotify().getContent());
        String authorization = MonitorJobContext.getInstance().getOndutyAuthorization();
        JsonObject json = MonitorJobContext.getInstance().getOndutyClient().appendComment(authorization, comment);
        JsonElement element = json == null ? null : json.get("ticket_id");
        return element == null ? null : element.getAsString();
    }

    private Notice buildSendInfo(MonitorEvent event) {
        MonitorStateEnum stateEnum;
        if (EventSourceEnum.LENS_EVENT.equals(event.getEventSourceEnum())) {
            stateEnum = event.getTriggered().getState();
        } else {
            stateEnum = MonitorStateEnum.valueOf(event.getState().name());
        }
        Integer maxLevel = null;
        if (stateEnum == MonitorStateEnum.NO_DATA) {
            Map<String, Object> thresholdMap = event.getLevelThresholdMap();
            if (!CollectionUtils.isEmpty(thresholdMap)) {
                Set<String> levels = thresholdMap.keySet();
                for (String l : levels) {
                    int i = Integer.valueOf(l);
                    if (maxLevel == null) {
                        maxLevel = i;
                    } else if (maxLevel < i) {
                        maxLevel = i;
                    }
                }
            }
        }
        Integer priority;
        if (maxLevel == null) {
            priority = stateEnum.getSeverity();
        } else {
            priority = MonitorStateEnum.valueOfLevel(maxLevel).getSeverity();
        }
        return Notice.builder()
                .agent(Agent.builder()
                        .agentId(MonitorJobContext.getInstance().getAgentId())
                        .queuedBy(MonitorJobContext.getInstance().getServiceType())
                        .build())
                .serviceKey(MonitorJobContext.getInstance().getServiceKey())
                .ctiId(new Gson().fromJson(event.getNotify().getChannelContent(), Onduty.class).getItem())
                .title(customOndutyTitle(event))
                .eventType("trigger")   //新报障
                .details(Detail.builder()
                        .eventId(event.getUid())
                        .priority(priority)
                        .content(getContent(event))
                        .alarmTime(new Date(event.getTimeStamp()))
                        .build())
                .build();
    }

    private String fireTicket(MonitorEvent event) {
        Notice message = buildSendInfo(event);
        Reply reply = MonitorJobContext.getInstance().getOndutyClient().sendAlarm(message);
        return reply == null ? null : reply.getTicketId();
    }

    private boolean saveTicket(String ticketId, MonitorEvent event) {
        LensMonitorChannelInVO inVO = LensMonitorChannelInVO.builder()
                .monitorId(event.getMonitorKey().getMonitorId())
                .version(event.getMonitorKey().getVersion())
                .target(BuilderUtil.DEFAULT_GROUP_VALUE)
                .type(ChannelEnum.ON_DUTY)
                .ticket(ticketId)
                .ticketTime(System.currentTimeMillis())
                .build();
        LensResponseData<Boolean> resp = MonitorJobContext.getInstance().getMonitorClient().saveMonitorChannel(inVO);
        return resp.getData() == null ? false : resp.getData();
    }

    private String customOndutyTitle(MonitorEvent event) {
        if (EventSourceEnum.EXTERNAL_EVENT.equals(event.getEventSourceEnum())) {
            return "External alert " + event.getNotify().getTitle();
        }
        StringBuilder builder = new StringBuilder();
        builder.append("Lens monitor ")
                .append(event.getMonitorName())
                .append(" generates a level")
                .append(event.getTriggered().getState().name())
                .append(" event ")
                .append(event.getNotify().getTitle());
        return builder.toString();
    }

    private String getContent(MonitorEvent event) {
        if (EventSourceEnum.LENS_EVENT.equals(event.getEventSourceEnum())) {
            return customOndutyContent(event);
        } else {
            StringBuilder builder = new StringBuilder();
            builder.append("External event: ").append(event.getNotify().getTitle()).append("\n");
            builder.append("Event Level: ").append(event.getState().name()).append("\n");
            builder.append("Event Content: ").append(event.getNotify().getContent());
            return builder.toString();
        }
    }

    private String customOndutyContent(MonitorEvent event) {
        StringBuilder builder = new StringBuilder();
        builder.append("Monitor Name: ").append(event.getMonitorName()).append("\n");
        builder.append("Event Level: ").append(event.getTriggered().getState().name()).append("\n");
        builder.append("Triggered Rule: ");
        AggregatorEnum aggrEnum = event.getAggrEnum();
        if (aggrEnum != null) {
            builder.append(aggrEnum).append("(");
        }
        builder.append(event.getMetricName());
        if (aggrEnum != null) {
            builder.append(")");
        }
        if (event.getOperatorEnum() != null) {
            builder.append(" ").append(event.getOperatorEnum().getOperator()).append(" ").append(event.getThreshold());
        }
        builder.append("\n");

        builder.append("Scope Fields: ");
        if (!CollectionUtils.isEmpty(event.getScope())) {
            builder.append(event.getScope());
        } else {
            builder.append("{}");
        }
        builder.append("\n");

        builder.append("Filter Fields: ");
        if (!CollectionUtils.isEmpty(event.getMetricFilter())) {
            builder.append(event.getMetricFilter());
        } else {
            builder.append("{}");
        }
        builder.append("\n");

        builder.append("Group By Fields: ");
        if (!BuilderUtil.DEFAULT_GROUP_VALUE.equalsIgnoreCase(event.getTriggered().getGroupValue())) {
            builder.append(event.getTriggered().getGroupValue());
        } else {
            builder.append("{}");
        }
        builder.append("\n");

        builder.append("Query Range: ").append(new Date(event.getTriggered().getStartTime()).toString()).append(" to ")
                .append(new Date(event.getTriggered().getEndTime()).toString()).append("\n");

//        String monitorUrl = MonitorJobContext.getInstance().getMonitorUrl();
//        builder.append("Event Detail: ").append(monitorUrl).append("/lens/getLensEventById?id=").append(event.getUid()).append("\n");
//        if (!StringUtils.isEmpty(event.getNotify().getContent())) {
//            builder.append(event.getNotify().getContent()).append("\n");
//        }
        return builder.toString();
    }
}
